Author

DaaniiH

Published

June 1, 2025

1 Libraries

Code anzeigen
library(readr)
library(readxl)
library(pxmake)   # to load PX files
library(pxR)      # to load PX files
library(jsonlite) # to load JSON files

library(purrr)

library(writexl)  # Write Excel files

library(dplyr)
library(tidyverse)
library(ggplot2)  # Diagrams
library(ggforce)  # Diagrams
library(DT)       # datatable()

library(DescTools)

2 Functions

Code anzeigen
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###  
# Duplikate in df finden & als $unique oder $duplicates selektionierbar machen
find_and_remove_duplicates <- function(df) {
  list(
    unique = df[!duplicated(df), ],
    duplicates = df[duplicated(df) | duplicated(df, fromLast = TRUE), ]
  )
}



### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###  
# Aktive National- & Ständeräte identifizieren und selektionierbar machen

filter_active <- function(data, von, bis) {
  von <- as.Date(von)
  bis <- as.Date(bis)
  data %>%
    filter(
      DateJoining <= bis,                      # Eintritt vor/am Ende d. Zeitraums
      is.na(DateLeaving) | DateLeaving >= von  # Kein Austritt/nach Beginn des Z..
    )
}




### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###  
# 

3 ETL: Extract, Transform, Load

  • Daten werden aus verschiedenen Excel-Dateien ausgelesen, unabhängig davon, wie komplex oder verschachtelt sie sind (z. B. mehrere Sheets, verbundene Zellen, unterteilte Datenblöcke).

  • Bei Excel-Dateien mit mehreren Sheets werden Functions angewendet.

  • Header werden identifiziert und ggf. aus mehreren Zeilen zusammengesetzt.

  • Daten werden bereinigt, normalisiert und in das gewünschte Zielformat gebracht (z. B. Wide zu Long, Entfernen von Leerzeilen, Auflösen von verbundenen Zellen, Vereinheitlichung der Spaltenstruktur).

  • Die transformierten Daten werden für die weitere Analyse und Visualisation zu Verfügung gestellt.

3.1 Abstimmungen

Code anzeigen
# Manuell migrierte Lookup Tabellen aus dem PDF-Codebook (swissvotes.ch) laden
# Parteien
lookups_codebook_parties <- read_excel("lookups_codebook.xlsx",
                                       sheet = "parties")
# Parteiparolen
lookups_codebook_paroles <- read_excel("lookups_codebook.xlsx",
                                       sheet = "paroles")


### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### 


# Ergbenisse der Schweizweite Abstimmungen laden
voting_raw <- read_delim("~/CAS/Zertifikatsarbeit/data/votes/abstimmungen_swissvotes_DATASET CSV 09-02-2025.csv",
                     delim = ";",
                     escape_double = FALSE,
                     trim_ws = TRUE,
                     show_col_types = FALSE)

# Datum formatieren
voting_raw <- voting_raw %>% 
  mutate(datum = dmy(datum))

# Einträge vor 2020 löschen
voting_5y <- voting_raw %>%
  filter(datum >= as.Date("2020-01-01"))

# Abstimmungsdaten der letzten 5 Jahre in Vektor schreiben für spätere Selektion
voting_5y_dates <- voting_5y %>%
  select(datum) %>%
  distinct() %>%
  pull(datum) %>%
  as.character()

# Abstimmungen ab 2020 in datatable() anzeigen
datatable(voting_5y,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))
Code anzeigen
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### 


# Parteien aus Parolen (p- & pdev-..-..) zwecks Lookup/Vereinheitlichung in df schreiben
lookup_voting_parties_label <- bind_rows(
  # Block 1: Staatsebene - Mutterparteien, Verbände & weitere Organisationen
  tibble(party_orig_value = grep("^p-",
                               names(voting_5y),
                               value = TRUE)) %>%
    mutate(level = "state") %>%
    separate(
      party_orig_value,
      into = c("prefix", "short_name", "add_info"),
      sep = "[-_]",
      remove = FALSE,
      extra = "merge",
      fill = "right"),
  # Block 2: Kantonsebene - Jungparteien, Frauensektionen, etc.
  tibble(party_orig_value = grep("^pdev-",
                               names(voting_5y),
                               value = TRUE)) %>%
    mutate(level = "canton_fraction") %>%
    separate(
      party_orig_value,
      into = c("prefix", "short_name", "add_info"),
      sep = "[-_]",
      remove = FALSE,
      extra = "merge",
      fill = "right")) %>%
  # Großschreibung der short_name-Spalte
  mutate(short_name = toupper(short_name),
         long_name = NA_character_,
         source = "Abstimmungen") %>% 
  select(party_orig_value,
         short_name,
         long_name,
         prefix,
         add_info,
         level,
         source)
  
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### 


# Relevante Felder selektionieren
voting_5y %>%
  select(anr,
         datum,
         titel_kurz_d,
         titel_off_d,
         rechtsform, #1 Obligatorisches Referendum
                     #2 Fakultatives Referendum
                     #3 Volksinitiative
                     #4 Direkter Gegenentwurf zu einer Volksinitiative
                     #5 Stichfrage
         dep,
         `br-pos`,   #1 Befürwortend
                     #2 Ablehnend
                     #3 Keine
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
                     #9 Vorzug für die Volksinitiative (bei Stichfragen)
                     #. Missing
         legisjahr,
         `pa-iv`,
         `bv-pos`,   #1 Befürwortend
                     #2 Ablehnend
                     #3 Keine Abstimmungsempfehlung des Parlaments
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
         `nr-pos`,   #1 Befürwortende Mehrheit im Nationalrat
                     #2 Ablehnende Mehrheit im Nationalrat
                     #3 Keine Abstimmungsempfehlung des Nationalrats
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
         nrja,
         nrnein,
         `sr-pos`,   #1 Befürwortende Mehrheit im Ständerat
                     #2 Ablehnende Mehrheit im Ständerat
                     #3 Keine Abstimmungsempfehlung des Ständerats
                     #8 Vorzug für den Gegenentwurf (bei Stichfragen)
         srja,
         srnein,
         # Parteiparolen
         starts_with("p-"),
                     #1 Ja-Parole
                     #2 Nein-Parole
                     #3 keine Parole abzugeben
                     #4 empfahl, einen leeren Stimmzettel einzulegen
                     #5 Stimmfreigabe
                     #8 Bevorzugung des Gegenentwurfs (bei Stichfrage)
                     #9 Bevorzugung der Volksinitiative (bei Stichfrage)
                     #66 Neutral: keine Parole oder Empfehlung auf leer einlegen                        #9999 Organisation existiert nicht
                     #. Unbekannt
         # Parteiparolen Kanton / Parteisektion
         starts_with("p-dev-"),
                     #1 Ja-Parole
                     #2 Nein-Parole
                     #3 keine Parole abzugeben
                     #4 empfahl, einen leeren Stimmzettel einzulegen
                     #5 Stimmfreigabe
                     #8 Bevorzugung des Gegenentwurfs (bei Stichfrage)
                     #9 Bevorzugung der Volksinitiative (bei Stichfrage)
                     # [leer] Parole gleich wie Mutterpartei (oder unbekannt)         
         volk,       #0 Eine Mehrheit der Abstimmenden hat die Vorlage abgelehnt
                     #1 Eine Mehrheit der Abstimmenden hat die Vorlage angenommen
                     #8 Bei Stichfragen:  Mehrheit für Gegenentwurfs
                     #9 Bei Stichfragen:  Mehrheit Volksinitiative
         stand,
                     #0 Die Vorlage hat keine Mehrheit der Standesstimmen er-reicht
                     #1 Die Vorlage hat die Mehrheit der Standesstimmen erreicht
                     #3 Ständemehr nicht notwendig
                     #8 Bei Stichfragen: Mehrheit für Gegenentwurf
                     #9 Bei Stichfragen: Mehrheit für Volksinitiative
         annahme,    #0 Ablehnung der Vorlage
                     #1 Annahme der Vorlage
                     #8 Bei Stichfragen: Gegenentwurf angenommen
                     #9 Bei Stichfragen: Volksinitiative angenommen
                     #. Bei Stichfragen: Ergebnis der Stichfrage obsolet
         berecht,
         stimmen,
         bet,
         leer,
         ungultig,
         gultig,
         volkja,
         volknein,
         `volkja-proz`)
# A tibble: 49 × 84
     anr datum      titel_kurz_d titel_off_d rechtsform dep   `br-pos` legisjahr
   <dbl> <date>     <chr>        <chr>            <dbl> <chr> <chr>    <chr>    
 1   629 2020-02-09 Initiative … Volksiniti…          3 6     2        2019-2023
 2   630 2020-02-09 Verbot der … Strafgeset…          2 3     1        2019-2023
 3   631 2020-09-27 Begrenzungs… Volksiniti…          3 3     2        2019-2023
 4   632 2020-09-27 Jagdgesetz   Änderung d…          2 7     1        2019-2023
 5   633 2020-09-27 Erhöhung de… Änderung d…          2 5     1        2019-2023
 6   634 2020-09-27 Vaterschaft… Änderung d…          2 2     1        2019-2023
 7   635 2020-09-27 Beschaffung… Bundesbesc…          2 4     1        2019-2023
 8   636 2020-11-29 Konzernvera… Volksiniti…          3 3     2        2019-2023
 9   637 2020-11-29 «Kriegsgesc… Volksiniti…          3 6     2        2019-2023
10   638 2021-03-07 Initiative … Volksiniti…          3 3     2        2019-2023
# ℹ 39 more rows
# ℹ 76 more variables: `pa-iv` <dbl>, `bv-pos` <dbl>, `nr-pos` <dbl>,
#   nrja <chr>, nrnein <chr>, `sr-pos` <dbl>, srja <chr>, srnein <chr>,
#   `p-fdp` <chr>, `p-sps` <chr>, `p-svp` <chr>, `p-mitte` <dbl>,
#   `p-evp` <chr>, `p-gps` <chr>, `p-glp` <dbl>, `p-pda` <chr>, `p-sd` <chr>,
#   `p-edu` <chr>, `p-fps` <chr>, `p-lega` <chr>, `p-kvp` <chr>, `p-mcg` <chr>,
#   `p-ucsp` <chr>, `p-cvp` <chr>, `p-bdp` <dbl>, `p-lps` <chr>, …

3.2 Wahlen

3.2.1 Bundesebene

3.2.1.1 Nationalrat

Code anzeigen
# Nationalrat: Daten einlesen
elec_nationalrat  <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/Ratsmitglieder_1848_DE_BUND_NR.xlsx")

# Struktur prüfen
str(elec_nationalrat)
tibble [492 × 21] (S3: tbl_df/tbl/data.frame)
 $ Active               : logi [1:492] TRUE FALSE FALSE TRUE TRUE FALSE ...
 $ FirstName            : chr [1:492] "Jean-Luc" "Jean-Luc" "Jean-Luc" "Cyril" ...
 $ LastName             : chr [1:492] "Addor" "Addor" "Addor" "Aellen" ...
 $ GenderAsString       : chr [1:492] "m" "m" "m" "m" ...
 $ CantonName           : chr [1:492] "Wallis" "Wallis" "Wallis" "Genf" ...
 $ CantonAbbreviation   : chr [1:492] "VS" "VS" "VS" "GE" ...
 $ CouncilName          : chr [1:492] "Nationalrat" "Nationalrat" "Nationalrat" "Nationalrat" ...
 $ ParlGroupName        : chr [1:492] "Fraktion der Schweizerischen Volkspartei" "Fraktion der Schweizerischen Volkspartei" "Fraktion der Schweizerischen Volkspartei" "FDP-Liberale Fraktion" ...
 $ ParlGroupAbbreviation: chr [1:492] "V" "V" "V" "RL" ...
 $ PartyName            : chr [1:492] "Schweizerische Volkspartei" "Schweizerische Volkspartei" "Schweizerische Volkspartei" "FDP.Die Liberalen" ...
 $ PartyAbbreviation    : chr [1:492] "SVP" "SVP" "SVP" "FDP-Liberale" ...
 $ MaritalStatusText    : chr [1:492] "verheiratet" "verheiratet" "verheiratet" NA ...
 $ Nationality          : chr [1:492] "Schweiz,Italien" "Schweiz,Italien" "Schweiz,Italien" "Schweiz" ...
 $ BirthPlace_City      : chr [1:492] "Lausanne" "Lausanne" "Lausanne" "Genf" ...
 $ BirthPlace_Canton    : chr [1:492] "Waadt" "Waadt" "Waadt" "Genf" ...
 $ Mandates             : chr [1:492] "Vice-président de l'UDC du Valais romand; Membre du Comité central de l'UDC Suisse; Député au Grand Conseil: 20"| __truncated__ "Vice-président de l'UDC du Valais romand; Membre du Comité central de l'UDC Suisse; Député au Grand Conseil: 20"| __truncated__ "Vice-président de l'UDC du Valais romand; Membre du Comité central de l'UDC Suisse; Député au Grand Conseil: 20"| __truncated__ "PLR Genève" ...
 $ DateJoining          : chr [1:492] "04.12.2023" "30.11.2015" "02.12.2019" "04.12.2023" ...
 $ DateLeaving          : chr [1:492] NA "01.12.2019" "03.12.2023" NA ...
 $ Citizenship          : chr [1:492] "Sainte-Croix (VD),Savièse (VS)" "Sainte-Croix (VD),Savièse (VS)" "Sainte-Croix (VD),Savièse (VS)" "Genf (GE)" ...
 $ DateOfBirth          : chr [1:492] "22.04.1964" "22.04.1964" "22.04.1964" "29.02.1972" ...
 $ DateOfDeath          : logi [1:492] NA NA NA NA NA NA ...
Code anzeigen
# Fehlende Werte (NA), Klassen und Levels prüfen
Abstract(elec_nationalrat)
────────────────────────────────────────────────────────────────────────────── 
elec_nationalrat

data frame: 492 obs. of  21 variables
        0 complete cases (0.0%)

  Nr  Class  ColName                NAs           Levels
  1   log    Active                   .                 
  2   chr    FirstName                .                 
  3   chr    LastName                 .                 
  4   chr    GenderAsString           .                 
  5   chr    CantonName               .                 
  6   chr    CantonAbbreviation       .                 
  7   chr    CouncilName              .                 
  8   chr    ParlGroupName            2 (0.4%)          
  9   chr    ParlGroupAbbreviation    2 (0.4%)          
  10  chr    PartyName                .                 
  11  chr    PartyAbbreviation        .                 
  12  chr    MaritalStatusText      324 (65.9%)         
  13  chr    Nationality              1 (0.2%)          
  14  chr    BirthPlace_City          2 (0.4%)          
  15  chr    BirthPlace_Canton       17 (3.5%)          
  16  chr    Mandates                47 (9.6%)          
  17  chr    DateJoining              .                 
  18  chr    DateLeaving            204 (41.5%)         
  19  chr    Citizenship              4 (0.8%)          
  20  chr    DateOfBirth              .                 
  21  log    DateOfDeath            492 (100.0%)        
Code anzeigen
PlotMiss(elec_nationalrat)    

Code anzeigen
# Duplikate mittels Funktion ermitteln
elec_nationalrat_duplicates <- find_and_remove_duplicates(elec_nationalrat)
    # elec_nationalrat_duplicates$unique
    # elec_nationalrat_duplicates$duplicates

# df mit unique Einträge weiterverwenden 
elec_nationalrat <- elec_nationalrat_duplicates$unique

# Auf relevante Spalten reduzieren und Datumsformat anpassen
    # Spaltenüberschriften konkateniert
    # cat(paste(paste0('"', colnames(elec_nationalrat), '"'),collapse = ",\n"))

elec_nationalrat_final <- elec_nationalrat %>% 
  select(# "Active",
         # "FirstName",
         # "LastName",
         # "GenderAsString",
         "CantonName",
         "CantonAbbreviation",
         # "CouncilName",
         # "ParlGroupName",
         # "ParlGroupAbbreviation",
         "PartyName",
         "PartyAbbreviation",
         # "MaritalStatusText",
         # "Nationality",
         # "BirthPlace_City",
         # "BirthPlace_Canton",
         # "Mandates",
         "DateJoining",
         "DateLeaving",
         # "Citizenship",
         # "DateOfBirth",
         "DateOfDeath") %>% 
  mutate(DateJoining = dmy(DateJoining),
         DateLeaving = dmy(DateLeaving),
         DateOfDeath = dmy(DateOfDeath))  

# Anzeige mit datatable()
datatable(elec_nationalrat_final,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

3.2.1.2 Ständerat

Code anzeigen
# Ständerat: Daten einlesen
elec_ständerat  <- read_excel("~/CAS/Zertifikatsarbeit/data/elections/Ratsmitglieder_1848_DE_BUND_SR.xlsx")

# Struktur prüfen
str(elec_ständerat)
tibble [155 × 21] (S3: tbl_df/tbl/data.frame)
 $ Active               : logi [1:155] TRUE FALSE TRUE FALSE FALSE FALSE ...
 $ FirstName            : chr [1:155] "Marianne" "Marianne" "Pirmin" "Pirmin" ...
 $ LastName             : chr [1:155] "Binder-Keller" "Binder-Keller" "Bischof" "Bischof" ...
 $ GenderAsString       : chr [1:155] "f" "f" "m" "m" ...
 $ CantonName           : chr [1:155] "Aargau" "Aargau" "Solothurn" "Solothurn" ...
 $ CantonAbbreviation   : chr [1:155] "AG" "AG" "SO" "SO" ...
 $ CouncilName          : chr [1:155] "Ständerat" "Nationalrat" "Ständerat" "Nationalrat" ...
 $ ParlGroupName        : chr [1:155] "Die Mitte-Fraktion. Die Mitte. EVP." "Die Mitte-Fraktion. Die Mitte. EVP." "Die Mitte-Fraktion. Die Mitte. EVP." "Fraktion CVP/EVP/glp" ...
 $ ParlGroupAbbreviation: chr [1:155] "M-E" "M-E" "M-E" "M-E" ...
 $ PartyName            : chr [1:155] "Die Mitte" "Christlichdemokratische Volkspartei der Schweiz" "Die Mitte" "Christlichdemokratische Volkspartei der Schweiz" ...
 $ PartyAbbreviation    : chr [1:155] "M-E" "CVP" "M-E" "CVP" ...
 $ MaritalStatusText    : chr [1:155] "verheiratet" "verheiratet" NA NA ...
 $ Nationality          : chr [1:155] "Schweiz" "Schweiz" "Schweiz" "Schweiz" ...
 $ BirthPlace_City      : chr [1:155] "Zürich" "Zürich" "Solothurn" "Solothurn" ...
 $ BirthPlace_Canton    : chr [1:155] "Zürich" "Zürich" "Solothurn" "Solothurn" ...
 $ Mandates             : chr [1:155] "Mitglied Präsidium Die Mitte Schweiz; Präsidentin Die Mitte Aargau" "Mitglied Präsidium Die Mitte Schweiz; Präsidentin Die Mitte Aargau" "Legislative des Kantons (Kantonsrat): von April 2005 bis November 2007; Exekutive der Gemeinde (Gemeinderat) So"| __truncated__ "Legislative des Kantons (Kantonsrat): von April 2005 bis November 2007; Exekutive der Gemeinde (Gemeinderat) So"| __truncated__ ...
 $ DateJoining          : chr [1:155] "04.12.2023" "02.12.2019" "04.12.2023" "03.12.2007" ...
 $ DateLeaving          : chr [1:155] NA "03.12.2023" NA "04.12.2011" ...
 $ Citizenship          : chr [1:155] "Baden (AG),Untersiggenthal (AG),Zurzach (Bad Zurzach) (AG)" "Baden (AG),Untersiggenthal (AG),Zurzach (Bad Zurzach) (AG)" "Eggersriet (SG),Grub (AR)" "Eggersriet (SG),Grub (AR)" ...
 $ DateOfBirth          : chr [1:155] "15.06.1958" "15.06.1958" "24.02.1959" "24.02.1959" ...
 $ DateOfDeath          : logi [1:155] NA NA NA NA NA NA ...
Code anzeigen
# Fehlende Werte (NA), Klassen und Levels prüfen
Abstract(elec_ständerat)
────────────────────────────────────────────────────────────────────────────── 
elec_ständerat

data frame: 155 obs. of  21 variables
        0 complete cases (0.0%)

  Nr  Class  ColName                NAs           Levels
  1   log    Active                   .                 
  2   chr    FirstName                .                 
  3   chr    LastName                 .                 
  4   chr    GenderAsString           .                 
  5   chr    CantonName               .                 
  6   chr    CantonAbbreviation       .                 
  7   chr    CouncilName              .                 
  8   chr    ParlGroupName            .                 
  9   chr    ParlGroupAbbreviation    .                 
  10  chr    PartyName                .                 
  11  chr    PartyAbbreviation        .                 
  12  chr    MaritalStatusText      116 (74.8%)         
  13  chr    Nationality              .                 
  14  chr    BirthPlace_City          .                 
  15  chr    BirthPlace_Canton        .                 
  16  chr    Mandates                11 (7.1%)          
  17  chr    DateJoining              .                 
  18  chr    DateLeaving             45 (29.0%)         
  19  chr    Citizenship              .                 
  20  chr    DateOfBirth              .                 
  21  log    DateOfDeath            155 (100.0%)        
Code anzeigen
PlotMiss(elec_ständerat)    

Code anzeigen
# Duplikate mittels Funktion ermitteln
elec_ständerat_duplicates <- find_and_remove_duplicates(elec_ständerat)
    # elec_nationalrat_duplicates$unique
    # elec_nationalrat_duplicates$duplicates

# df mit unique Einträge weiterverwenden 
elec_ständerat <- elec_ständerat_duplicates$unique

  
# Auf relevante Spalten reduzieren und Datumsformat anpassen
    # Spaltenüberschriften konkateniert
    # cat(paste(paste0('"', colnames(elec_nationalrat), '"'),collapse = ",\n"))


elec_ständerat_final <- elec_ständerat %>% 
  select(# "Active",
         # "FirstName",
         # "LastName",
         # "GenderAsString",
         "CantonName",
         "CantonAbbreviation",
         # "CouncilName",
         # "ParlGroupName",
         # "ParlGroupAbbreviation",
         "PartyName",
         "PartyAbbreviation",
         # "MaritalStatusText",
         # "Nationality",
         # "BirthPlace_City",
         # "BirthPlace_Canton",
         # "Mandates",
         "DateJoining",
         "DateLeaving",
         # "Citizenship",
         # "DateOfBirth",
         "DateOfDeath") %>% 
  mutate(DateJoining = dmy(DateJoining),
         DateLeaving = dmy(DateLeaving),
         DateOfDeath = dmy(DateOfDeath))


# Anzeige mit datatable()
datatable(elec_ständerat_final,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

3.2.1.3 National- & Ständerat kombinieren für weitere Bearbeitung

Code anzeigen
# National- und Ständeratlisten kombinieren
elec_nr_sr_combined <- bind_rows(
  elec_nationalrat_final %>% mutate(role = "Nationalrat"),
  elec_ständerat_final %>% mutate(role = "Ständerat")) %>%
  select(role, everything())


# Abstiummungsdatum (voting_5y_dates) als Spalten hinzufügen
elec_nr_sr_combined <- elec_nr_sr_combined %>%
  mutate(!!!setNames(rep(list(NA_integer_),
                         length(voting_5y_dates)),
                     voting_5y_dates))

# Ergänzen ob Ratsmitglied zum Zeitpunkt der Abstimmung aktiv war
elec_nr_sr_combined <- elec_nr_sr_combined %>%
  mutate(!!!setNames(
    lapply(voting_5y_dates,
           function(datum) {
             abstimmungsdatum <- as.Date(datum)
             with(., as.integer(                            # 1 der 0 zurück
               DateJoining <= abstimmungsdatum &
                 (is.na(DateLeaving) | DateLeaving >= abstimmungsdatum)))}
           ), voting_5y_dates))


### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### 


# Kanton- und Parteibezeichnungen zwecks Lookup/Vereinheitlichung in df schreiben 
lookup_elec_nr_sr_combined_canton_label <- elec_nr_sr_combined %>% 
  select(CantonAbbreviation, CantonName) %>% 
  distinct()


lookup_elec_nr_sr_combined_parties_label  <- elec_nr_sr_combined %>%
  select(PartyAbbreviation, PartyName) %>%
  distinct() %>%
  rename(party_orig_value = PartyAbbreviation,
         long_name = PartyName) %>%
  mutate(prefix = NA_character_,
         add_info = NA,
         level = NA_character_,
         source = "National_und_Ständerat",
         short_name = party_orig_value %>%
           str_replace("-.*", "") %>%         # ab dem ersten "-" abschneiden
           str_replace("^M-E$", "Mitte") %>%  # "M-E" durch "Mitte" ersetzen
         toupper())%>% 
  select(party_orig_value,
         short_name,
         long_name,
         prefix,
         add_info,
         level,
         source)

3.2.2 Kantonsebene

3.2.2.1 Kantonale Regierung (Exekutive)

Die Kantonalen Abstimmungen finden nicht in allen kantonen gleichzeitg statt. Deshalb genauer die Räte/Konstellation zum Zeitpunkt der jeweiligen Abstimmung zu prüfen.

Die Struktur der “schön formatierten” Exceldatei lässt keinen “simplen” Import zu.

  • 1 Excelsheet / Jahr

  • Header ist in Zeile 2 und nicht vollständig

  • Daten (für Kantone) starten in Zeile 4 aber enden auf Zeile 29 bevor es mit Kommentaren und Fussnoten weitergeht.

Dateipfad und gewünschte Sheets festlegen

Code anzeigen
dateipfad <- "~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.06.01_KANTON_Kantonale_Regierungswahlen.xlsx"  

selected_sheets <- c("2024", "2023", "2022","2021","2020","2019")

Function

Code anzeigen
import_election_data <- function(dateipfad, sheetname) {
  
  # Headerzeilen einlesen  
  header <- readxl::read_excel(dateipfad,
                               sheet = sheetname,
                               skip = 1,
                               n_max = 0) %>%
    names()
  
  # Header anpassen: Wahljahr vereinheitlichen und Kanton hinzufügen
  header[grepl("^Wahljahr", header)] <- "Wahljahr" 
  header <- c("Kanton", header)
        
  # Daten ab Zeile 4 importieren
  daten_raw <- read_excel(dateipfad,
                          sheet = sheetname,
                          skip = 3,
                          col_names = header)
  
  # Zeilen ohne "Wahljahr" ausschliessen
  wahljahr_col <- names(daten_raw)[grepl("^Wahljahr",
                                         names(daten_raw))][1]
  names(daten_raw)[names(daten_raw) == wahljahr_col] <- "Wahljahr"

  daten_wide <- daten_raw %>%
    filter(!is.na(suppressWarnings(as.numeric(.data[[wahljahr_col]]))))
  
  # Zu pivotierenden Spalten in numeric umwandeln
  cols_pivot <- setdiff(names(daten_wide), c("Kanton", wahljahr_col))
  daten_wide <- daten_wide %>%
    mutate(across(all_of(cols_pivot), as.numeric))
  
  # 6. Pivotieren
  daten_long <- daten_wide %>%
    pivot_longer(
      cols = all_of(cols_pivot),
      names_to = "Partei",
      values_to = "Wert")
  
  return(daten_long)
}

Function ausführen: Import und Konsolidation

Code anzeigen
# Objekt erstellen, mit Sheets als tibble/dataframe
daten_liste <- setNames(lapply(selected_sheets,
                               function(sheet) import_election_data (dateipfad,
                                                                     sheet)),
                        selected_sheets)

str(daten_liste)
List of 6
 $ 2024: tibble [494 × 4] (S3: tbl_df/tbl/data.frame)
  ..$ Kanton  : chr [1:494] "Zürich" "Zürich" "Zürich" "Zürich" ...
  ..$ Wahljahr: num [1:494] 2023 2023 2023 2023 2023 ...
  ..$ Partei  : chr [1:494] "FDP 2" "SP" "SVP" "LP 2" ...
  ..$ Wert    : num [1:494] 1 1 2 NA 0 NA 0 1 NA NA ...
 $ 2023: tibble [494 × 4] (S3: tbl_df/tbl/data.frame)
  ..$ Kanton  : chr [1:494] "Zürich" "Zürich" "Zürich" "Zürich" ...
  ..$ Wahljahr: num [1:494] 2023 2023 2023 2023 2023 ...
  ..$ Partei  : chr [1:494] "FDP 2)" "SP" "SVP" "LP 2)" ...
  ..$ Wert    : num [1:494] 1 1 2 NA 0 NA 0 1 NA NA ...
 $ 2022: tibble [494 × 4] (S3: tbl_df/tbl/data.frame)
  ..$ Kanton  : chr [1:494] "Zürich" "Zürich" "Zürich" "Zürich" ...
  ..$ Wahljahr: num [1:494] 2019 2019 2019 2019 2019 ...
  ..$ Partei  : chr [1:494] "FDP 2)" "SP" "SVP" "LP 2)" ...
  ..$ Wert    : num [1:494] 1 1 2 NA 0 NA NA NA 1 0 ...
 $ 2021: tibble [494 × 4] (S3: tbl_df/tbl/data.frame)
  ..$ Kanton  : chr [1:494] "Zürich" "Zürich" "Zürich" "Zürich" ...
  ..$ Wahljahr: num [1:494] 2019 2019 2019 2019 2019 ...
  ..$ Partei  : chr [1:494] "FDP 2)" "SP" "SVP" "LP 2)" ...
  ..$ Wert    : num [1:494] 1 1 2 NA 0 NA NA NA 1 0 ...
 $ 2020: tibble [780 × 4] (S3: tbl_df/tbl/data.frame)
  ..$ Kanton  : chr [1:780] "Zürich" "Zürich" "Zürich" "Zürich" ...
  ..$ Wahljahr: num [1:780] 2019 2019 2019 2019 2019 ...
  ..$ Partei  : chr [1:780] "FDP 2)" "CVP 3)" "SP" "SVP" ...
  ..$ Wert    : num [1:780] 1 1 1 2 NA NA NA 0 NA NA ...
 $ 2019: tibble [780 × 4] (S3: tbl_df/tbl/data.frame)
  ..$ Kanton  : chr [1:780] "Zürich" "Zürich" "Zürich" "Zürich" ...
  ..$ Wahljahr: num [1:780] 2019 2019 2019 2019 2019 ...
  ..$ Partei  : chr [1:780] "FDP 2)" "CVP 3)" "SP" "SVP" ...
  ..$ Wert    : num [1:780] 1 1 2 2 NA NA NA 0 NA NA ...
Code anzeigen
lapply(daten_liste, summary)
$`2024`
    Kanton             Wahljahr       Partei               Wert      
 Length:494         Min.   :2020   Length:494         Min.   :0.000  
 Class :character   1st Qu.:2022   Class :character   1st Qu.:0.000  
 Mode  :character   Median :2023   Mode  :character   Median :1.000  
                    Mean   :2023                      Mean   :1.613  
                    3rd Qu.:2024                      3rd Qu.:2.000  
                    Max.   :2024                      Max.   :7.000  
                                                      NA's   :303    

$`2023`
    Kanton             Wahljahr       Partei               Wert      
 Length:494         Min.   :2020   Length:494         Min.   :0.000  
 Class :character   1st Qu.:2020   Class :character   1st Qu.:0.000  
 Mode  :character   Median :2022   Mode  :character   Median :1.000  
                    Mean   :2022                      Mean   :1.647  
                    3rd Qu.:2023                      3rd Qu.:2.000  
                    Max.   :2023                      Max.   :7.000  
                                                      NA's   :307    

$`2022`
    Kanton             Wahljahr       Partei               Wert      
 Length:494         Min.   :2018   Length:494         Min.   :0.000  
 Class :character   1st Qu.:2020   Class :character   1st Qu.:0.000  
 Mode  :character   Median :2020   Mode  :character   Median :1.000  
                    Mean   :2020                      Mean   :1.613  
                    3rd Qu.:2022                      3rd Qu.:2.000  
                    Max.   :2022                      Max.   :7.000  
                                                      NA's   :303    

$`2021`
    Kanton             Wahljahr       Partei               Wert      
 Length:494         Min.   :2017   Length:494         Min.   :0.000  
 Class :character   1st Qu.:2018   Class :character   1st Qu.:0.000  
 Mode  :character   Median :2020   Mode  :character   Median :1.000  
                    Mean   :2019                      Mean   :1.613  
                    3rd Qu.:2020                      3rd Qu.:2.000  
                    Max.   :2021                      Max.   :7.000  
                                                      NA's   :303    

$`2020`
    Kanton             Wahljahr       Partei               Wert      
 Length:780         Min.   :2016   Length:780         Min.   :0.000  
 Class :character   1st Qu.:2018   Class :character   1st Qu.:0.000  
 Mode  :character   Median :2019   Mode  :character   Median :1.000  
                    Mean   :2019                      Mean   :1.604  
                    3rd Qu.:2020                      3rd Qu.:2.000  
                    Max.   :2020                      Max.   :7.000  
                                                      NA's   :588    

$`2019`
    Kanton             Wahljahr       Partei               Wert      
 Length:780         Min.   :2015   Length:780         Min.   :0.000  
 Class :character   1st Qu.:2016   Class :character   1st Qu.:0.000  
 Mode  :character   Median :2018   Mode  :character   Median :1.000  
                    Mean   :2017                      Mean   :1.596  
                    3rd Qu.:2018                      3rd Qu.:2.000  
                    Max.   :2019                      Max.   :7.000  
                                                      NA's   :587    
Code anzeigen
# Alle tibbles zu einem Dataframe zusammenfügen
elec_kantonale_regierung_combined <- bind_rows(daten_liste,
                                               .id = "Sheet")

# Parteinamenvarianten
unique(elec_kantonale_regierung_combined$Partei)
 [1] "FDP 2"        "SP"           "SVP"          "LP 2"         "EVP"         
 [6] "CSP"          "GLP"          "Die Mitte 3"  "CVP 3"        "BDP 3"       
[11] "PdA"          "PSA"          "Grüne 5"      "FGA"          "Sol."        
[16] "Lega"         "MCG (MCR)"    "Übrige 4"     "Total"        "FDP 2)"      
[21] "LP 2)"        "Die Mitte 3)" "CVP 3)"       "BDP 5)"       "Grüne 6)"    
[26] "MCR"          "Übrige 4)"    "GPS"          "Dem."         "LdU"         
[31] "DSP"          "BDP"          "POCH"         "SD"           "Rep."        
[36] "EDU"          "FPS"          "LS"           "JB"           "Front"       
[41] "Grüt"         "GP"          
Code anzeigen
# Parteien zwecks Lookup/Vereinheitlichung in df schreiben
lookup_kantonale_regierung_parties_label <- elec_kantonale_regierung_combined %>%
  rename(party_orig_value = Partei) %>%
  mutate(level = NA_character_,
         long_name = NA_character_,
         source = "Kantonale_Regierung",
         
         add_info = str_extract(party_orig_value,
                                "\\d+(?=\\)?$)"),  # Zahl am Ende/vor Klammer-Ende
         short_name = str_remove(party_orig_value,
                                 "\\s*\\d+\\)?$"), # - Leerzeichen/Zahl/Klammer
         prefix = NA_character_) %>%
  select(party_orig_value,
         short_name,
         long_name,
         prefix,
         add_info,
         level,
         source) %>%
  # Großschreibung der short_name-Spalte
  mutate(short_name = toupper(short_name))


# Output prüfen (unique/distinct)
lookup_kantonale_regierung_parties_label %>% distinct() %>% print(n = Inf)
# A tibble: 42 × 7
   party_orig_value short_name long_name prefix add_info level source           
   <chr>            <chr>      <chr>     <chr>  <chr>    <chr> <chr>            
 1 FDP 2            FDP        <NA>      <NA>   2        <NA>  Kantonale_Regier…
 2 SP               SP         <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
 3 SVP              SVP        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
 4 LP 2             LP         <NA>      <NA>   2        <NA>  Kantonale_Regier…
 5 EVP              EVP        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
 6 CSP              CSP        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
 7 GLP              GLP        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
 8 Die Mitte 3      DIE MITTE  <NA>      <NA>   3        <NA>  Kantonale_Regier…
 9 CVP 3            CVP        <NA>      <NA>   3        <NA>  Kantonale_Regier…
10 BDP 3            BDP        <NA>      <NA>   3        <NA>  Kantonale_Regier…
11 PdA              PDA        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
12 PSA              PSA        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
13 Grüne 5          GRÜNE      <NA>      <NA>   5        <NA>  Kantonale_Regier…
14 FGA              FGA        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
15 Sol.             SOL.       <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
16 Lega             LEGA       <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
17 MCG (MCR)        MCG (MCR)  <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
18 Übrige 4         ÜBRIGE     <NA>      <NA>   4        <NA>  Kantonale_Regier…
19 Total            TOTAL      <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
20 FDP 2)           FDP        <NA>      <NA>   2        <NA>  Kantonale_Regier…
21 LP 2)            LP         <NA>      <NA>   2        <NA>  Kantonale_Regier…
22 Die Mitte 3)     DIE MITTE  <NA>      <NA>   3        <NA>  Kantonale_Regier…
23 CVP 3)           CVP        <NA>      <NA>   3        <NA>  Kantonale_Regier…
24 BDP 5)           BDP        <NA>      <NA>   5        <NA>  Kantonale_Regier…
25 Grüne 6)         GRÜNE      <NA>      <NA>   6        <NA>  Kantonale_Regier…
26 MCR              MCR        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
27 Übrige 4)        ÜBRIGE     <NA>      <NA>   4        <NA>  Kantonale_Regier…
28 GPS              GPS        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
29 Dem.             DEM.       <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
30 LdU              LDU        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
31 DSP              DSP        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
32 BDP              BDP        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
33 POCH             POCH       <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
34 SD               SD         <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
35 Rep.             REP.       <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
36 EDU              EDU        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
37 FPS              FPS        <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
38 LS               LS         <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
39 JB               JB         <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
40 Front            FRONT      <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
41 Grüt             GRÜT       <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
42 GP               GP         <NA>      <NA>   <NA>     <NA>  Kantonale_Regier…
Code anzeigen
datatable(elec_kantonale_regierung_combined,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))

3.2.3 Gemeindeebene

3.2.3.1 Exekutiven der statistischen Städte

  • 1 Excelsheet / Jahr

  • Header zweizeilig mit Start in Zeile –> 3 skip = 2, n_max = 2,

  • Teils verbundene Header-Zeilen (vertikal/horizontal)

  • Cluster Einwohnerzahlen unterteilt Daten

  • Parteinamen uneinheitlich aufgrund von Fussnoten

–> Datenformat “wide” zuerst bereinigen und ins “long” Format bringen

Dateipfad und gewünschte Sheets festlegen

Code anzeigen
dateipfad <- "~/CAS/Zertifikatsarbeit/data/elections/je-d-17.02.07.01_GEMEINDE_Die Exekutiven der statistischen Städte.xlsx"

selected_sheets <- c("2024", "2023", "2022","2021","2020","2019") 

Function für den Import der Gemeindedaten

Code anzeigen
importiere_sheet <- function(dateipfad, sheetname) {

      ###################################################################
      ########## DATEN IMPORTIEREN ######################################
      ########## Header definieren ######################################
      ###################################################################
      
      
      # Headerzeilen einlesen
      header <- suppressMessages(read_excel(dateipfad,
                   sheet = sheetname,
                   skip = 2,
                   n_max = 2,
                   col_names = FALSE))
      
      # Daten  einlesen
      daten_raw <- suppressMessages(read_excel(dateipfad,
                              sheet = sheetname,
                              skip = 5,
                              col_names = FALSE))
      
      
      # Die Spaltennamen zusammensetzen
      # Leere Zellen in 1. Headerzeile mit Werten von rechts auffüllen wenn leer/NA
      header_filled <- as.data.frame(t(header))   # t() "matrix transpose"
      header_filled <- fill(header_filled,        
                            V1,                   # Header Spalte 1 (V1) auffüllen
                            .direction = "down")  # Werte nach unten übernehmen
      header_filled <- t(header_filled)           # t() nochmals (zurück)
      
      # Spaltennamen kombinieren
      spaltennamen <- paste(header_filled[1, ],
                            header_filled[2, ],
                            sep = "_")
      spaltennamen <- gsub("_NA|NA_","", spaltennamen) # Entfernt überflüssige NAs
      
      # Spaltennamen zuweisen
      colnames(daten_raw) <- spaltennamen
      
      
      ###################################################################
      ########## TRANSPONIEREN ##########################################
      ########## Gemeindegrössen Cluster ################################
      ###################################################################
      
      
      # Cluster Einwohnerzahl als Spalte verwenden
      daten_wide <- daten_raw %>%
        mutate(Gemeindegrösse_Cluster =             # Name der neuen Spalte
                 ifelse(
                   is.na(                           # 4) auf NA prüfen
                     suppressWarnings(        # 3) Warnung aus (z.B. "≥ ..Einw..")
                     as.numeric(                    # 2) Wert als Zahl 
                     gsub("'", "", `Kantons-Nr.`)   # 1) Tausender Zeichen (')                                                              entfernen
                   ))),
                   as.character(`Kantons-Nr.`),     # 5) Wert von "Kantons-Nr" 
                   NA_character_                    #    sonst NA   
                 ))  %>%
        fill(Gemeindegrösse_Cluster,
             .direction = "down") %>%              # Cluster nach unten auffüllen
        filter(!is.na(suppressWarnings(
          as.numeric(gsub("'", "", `Kantons-Nr.`))))) # Herausfiltern von Zeilen                                                           mit NICHT-numerischem Wert
    
    
    # Ansicht (Snapshot) nach transponieren
    daten_wide[1:10,                    # 10 Zeilen
               c(1:10,                  # 10 Spalten +
                 ncol(daten_wide))]     # Letzte Spalte
                                        # ncol() von df daten_wide --> Anz. Spalten
                                        # Anzahl Spalten = Position letzte Spalte
    
    
    
    ###################################################################
    ########## TRANSPONIEREN ##########################################
    ########## Frauen, Männer, Total & Parteien ## ####################
    ###################################################################
    
    
    
    daten_long <- daten_wide %>%
      mutate(across(matches("(_Frauen|_Männer|_Total)$"),
                    ~ suppressWarnings(as.numeric(.)))) %>%     # Werte numerisch
      pivot_longer(cols = matches("(_Frauen|_Männer|_Total)$"), # Spaltenauswahl
                   names_to = c("Partei", "m_w_Total"),         # Neue Label-Spalte
                   names_pattern = "^(.*)_(Frauen|Männer|Total)$",  # RegEx
                   values_to = "Wert")                          # Neue Wert-Spalte
    
    return(daten_long)

}

Function ausführen: Import der Dateien testen

Code anzeigen
for (sheet in selected_sheets) {
  cat("\n-----------------------------\n")
  cat("Versuche Sheet:", sheet, "\n")
  result <- tryCatch({
    dat <- importiere_sheet(dateipfad, sheet)
    cat("Sheet erfolgreich eingelesen:", sheet, "\n")
    # Zusammenfassung:
    cat("Anzahl Zeilen:", nrow(dat), "\n")
    cat("Anzahl Spalten:", ncol(dat), "\n")
    cat("Spaltennamen (erste 15):", paste(names(dat)[1:min(15, ncol(dat))], collapse = ", "), "\n")
    TRUE
  }, error = function(e) {
    cat("Fehler beim Einlesen von Sheet:", sheet, "\n")
    cat("Fehlermeldung:", e$message, "\n")
    FALSE
  })
}

-----------------------------
Versuche Sheet: 2024 
Sheet erfolgreich eingelesen: 2024 
Anzahl Zeilen: 7515 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2023 
Sheet erfolgreich eingelesen: 2023 
Anzahl Zeilen: 7776 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2022 
Sheet erfolgreich eingelesen: 2022 
Anzahl Zeilen: 7776 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2021 
Sheet erfolgreich eingelesen: 2021 
Anzahl Zeilen: 7776 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2020 
Sheet erfolgreich eingelesen: 2020 
Anzahl Zeilen: 7290 
Anzahl Spalten: 10 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Wahljahr, Einwohner, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

-----------------------------
Versuche Sheet: 2019 
Sheet erfolgreich eingelesen: 2019 
Anzahl Zeilen: 14580 
Anzahl Spalten: 11 
Spaltennamen (erste 15): Kantons-Nr., Kanton, Gemeinde-Nr., Gemeinde, Einwohner, Grössenklasse-Nr., Grössenklasse, Gemeindegrösse_Cluster, Partei, m_w_Total, Wert 

Function ausführen: Import und Konsolidation

Code anzeigen
# Objekt erstellen, mit Sheets als tibble/dataframe
daten_liste <- setNames(lapply(selected_sheets,
                               function(sheet) importiere_sheet(dateipfad, sheet)),
                        selected_sheets)

str(daten_liste)
List of 6
 $ 2024: tibble [7,515 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7515] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7515] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7515] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7515] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7515] 2022 2022 2022 2022 2022 ...
  ..$ Einwohner             : num [1:7515] 115129 115129 115129 115129 115129 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7515] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7515] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7515] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7515] 0 1 1 0 1 1 NA NA NA 1 ...
 $ 2023: tibble [7,776 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7776] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7776] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7776] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7776] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7776] 2022 2022 2022 2022 2022 ...
  ..$ Einwohner             : num [1:7776] 115129 115129 115129 115129 115129 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7776] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7776] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7776] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7776] 0 1 1 0 1 1 NA NA NA 1 ...
 $ 2022: tibble [7,776 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7776] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7776] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7776] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7776] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7776] 2022 2022 2022 2022 2022 ...
  ..$ Einwohner             : num [1:7776] 115129 115129 115129 115129 115129 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7776] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7776] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7776] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7776] 0 1 1 0 1 1 NA NA NA 1 ...
 $ 2021: tibble [7,776 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7776] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7776] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7776] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7776] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7776] 2018 2018 2018 2018 2018 ...
  ..$ Einwohner             : num [1:7776] 110912 110912 110912 110912 110912 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7776] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7776] "FDP 1" "FDP 1" "FDP 1" "Die Mitte 2" ...
  ..$ m_w_Total             : chr [1:7776] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7776] 1 1 2 NA NA NA 0 1 1 2 ...
 $ 2020: tibble [7,290 × 10] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:7290] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:7290] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:7290] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:7290] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Wahljahr              : num [1:7290] 2018 2018 2018 2018 2018 ...
  ..$ Einwohner             : num [1:7290] 110912 110912 110912 110912 110912 ...
  ..$ Gemeindegrösse_Cluster: chr [1:7290] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:7290] "FDP 1)" "FDP 1)" "FDP 1)" "CVP" ...
  ..$ m_w_Total             : chr [1:7290] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:7290] 1 1 2 0 1 1 2 1 3 NA ...
 $ 2019: tibble [14,580 × 11] (S3: tbl_df/tbl/data.frame)
  ..$ Kantons-Nr.           : chr [1:14580] "1" "1" "1" "1" ...
  ..$ Kanton                : chr [1:14580] "ZH" "ZH" "ZH" "ZH" ...
  ..$ Gemeinde-Nr.          : num [1:14580] 230 230 230 230 230 230 230 230 230 230 ...
  ..$ Gemeinde              : chr [1:14580] "Winterthur" "Winterthur" "Winterthur" "Winterthur" ...
  ..$ Einwohner             : num [1:14580] 111851 111851 111851 111851 111851 ...
  ..$ Grössenklasse-Nr.     : chr [1:14580] "4" "4" "4" "4" ...
  ..$ Grössenklasse         : chr [1:14580] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Gemeindegrösse_Cluster: chr [1:14580] "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" "≥ 100'000 Einwohnerinnen und Einwohner" ...
  ..$ Partei                : chr [1:14580] "FDP 1)" "FDP 1)" "FDP 1)" "CVP" ...
  ..$ m_w_Total             : chr [1:14580] "Frauen" "Männer" "Total" "Frauen" ...
  ..$ Wert                  : num [1:14580] 1 1 2 0 1 1 2 1 3 NA ...
Code anzeigen
lapply(daten_liste, summary)
$`2024`
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7515        Length:7515        Min.   :   2   Length:7515       
 Class :character   Class :character   1st Qu.: 581   Class :character  
 Mode  :character   Mode  :character   Median :2769   Mode  :character  
                                       Mean   :2936                     
                                       3rd Qu.:5113                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2020   Min.   :  4957   Length:7515            Length:7515       
 1st Qu.:2021   1st Qu.: 11680   Class :character       Class :character  
 Median :2022   Median : 15763   Mode  :character       Mode  :character  
 Mean   :2022   Mean   : 25286                                            
 3rd Qu.:2024   3rd Qu.: 22110                                            
 Max.   :2024   Max.   :423193                                            
                                                                          
  m_w_Total              Wert      
 Length:7515        Min.   : 0.00  
 Class :character   1st Qu.: 1.00  
 Mode  :character   Median : 1.00  
                    Mean   : 1.73  
                    3rd Qu.: 2.00  
                    Max.   :30.00  
                    NA's   :4986   

$`2023`
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7776        Length:7776        Min.   :   2   Length:7776       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2952                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2020   Min.   :  4957   Length:7776            Length:7776       
 1st Qu.:2020   1st Qu.: 12104   Class :character       Class :character  
 Median :2021   Median : 15754   Mode  :character       Mode  :character  
 Mean   :2021   Mean   : 25369                                            
 3rd Qu.:2022   3rd Qu.: 22193                                            
 Max.   :2023   Max.   :423193                                            
                                                                          
  m_w_Total              Wert       
 Length:7776        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.719  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :5316    

$`2022`
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7776        Length:7776        Min.   :   2   Length:7776       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2952                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2019   Min.   :  4957   Length:7776            Length:7776       
 1st Qu.:2020   1st Qu.: 12104   Class :character       Class :character  
 Median :2021   Median : 15754   Mode  :character       Mode  :character  
 Mean   :2021   Mean   : 25353                                            
 3rd Qu.:2022   3rd Qu.: 22193                                            
 Max.   :2022   Max.   :423193                                            
                                                                          
  m_w_Total              Wert       
 Length:7776        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.725  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :5325    

$`2021`
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7776        Length:7776        Min.   :   2   Length:7776       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2952                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2017   Min.   :  4994   Length:7776            Length:7776       
 1st Qu.:2018   1st Qu.: 12068   Class :character       Class :character  
 Median :2020   Median : 15754   Mode  :character       Mode  :character  
 Mean   :2020   Mean   : 25043                                            
 3rd Qu.:2021   3rd Qu.: 21718                                            
 Max.   :2021   Max.   :409241                                            
                                                                          
  m_w_Total              Wert       
 Length:7776        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.722  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :5316    

$`2020`
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:7290        Length:7290        Min.   :   2   Length:7290       
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2951                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
    Wahljahr      Einwohner      Gemeindegrösse_Cluster    Partei         
 Min.   :2016   Min.   :  4994   Length:7290            Length:7290       
 1st Qu.:2017   1st Qu.: 11572   Class :character       Class :character  
 Median :2018   Median : 15710   Mode  :character       Mode  :character  
 Mean   :2018   Mean   : 24871                                            
 3rd Qu.:2020   3rd Qu.: 21090                                            
 Max.   :2020   Max.   :409241                                            
                                                                          
  m_w_Total              Wert       
 Length:7290        Min.   : 0.000  
 Class :character   1st Qu.: 1.000  
 Mode  :character   Median : 1.000  
                    Mean   : 1.717  
                    3rd Qu.: 2.000  
                    Max.   :30.000  
                    NA's   :4827    

$`2019`
 Kantons-Nr.           Kanton           Gemeinde-Nr.    Gemeinde        
 Length:14580       Length:14580       Min.   :   2   Length:14580      
 Class :character   Class :character   1st Qu.: 546   Class :character  
 Mode  :character   Mode  :character   Median :2770   Mode  :character  
                                       Mean   :2951                     
                                       3rd Qu.:5192                     
                                       Max.   :6711                     
                                                                        
   Einwohner      Grössenklasse-Nr.  Grössenklasse      Gemeindegrösse_Cluster
 Min.   :  4928   Length:14580       Length:14580       Length:14580          
 1st Qu.: 11733   Class :character   Class :character   Class :character      
 Median : 15693   Mode  :character   Mode  :character   Mode  :character      
 Mean   : 25006                                                               
 3rd Qu.: 21506                                                               
 Max.   :415367                                                               
                                                                              
    Partei           m_w_Total              Wert      
 Length:14580       Length:14580       Min.   : 0.00  
 Class :character   Class :character   1st Qu.: 0.00  
 Mode  :character   Mode  :character   Median : 0.00  
                                       Mean   : 0.65  
                                       3rd Qu.: 1.00  
                                       Max.   :30.00  
                                       NA's   :8084   
Code anzeigen
# Alle tibbles zu einem Dataframe zusammenfügen
elec_gemeinde_exekutiven_combined <- bind_rows(daten_liste,
                                            .id = "Sheet") %>% 
  select(-c('Grössenklasse-Nr.', Grössenklasse))



datatable(elec_gemeinde_exekutiven_combined,
          class = 'nowrap',
          filter = 'top',
          options = list(pageLength = 7,
                         scrollX = TRUE,
                         search = list(regex = TRUE,
                                       caseInsensitive = TRUE)))
Code anzeigen
# Parteinamenvarianten
unique(elec_gemeinde_exekutiven_combined$Partei)
 [1] "FDP 1"       "Die Mitte 2" "CVP 2"       "SP"          "SVP"        
 [6] "LP 1"        "EVP"         "CSP"         "GLP"         "PdA"        
[11] "GRÜNE 3"     "EDU"         "Lega"        "Übrige"      "Total"      
[16] "BDP 2"       "FDP 1)"      "CVP"         "LP 1)"       "BDP"        
[21] "GPS"         "Dem."        "LdU"         "DSP"         "PSA"        
[26] "POCH"        "GA"          "Sol."        "SD"          "Rep."       
[31] "FPS"         "MCR"         "LS"          "JB"          "Front"      
[36] "Grüt"       
Code anzeigen
# Parteien zwecks Lookup/Vereinheitlichung in df schreiben
lookup_gemeinde_exekutiven_parties_label <- elec_gemeinde_exekutiven_combined %>%
  rename(party_orig_value = Partei) %>%
  mutate(add_info = str_extract(party_orig_value,
                                "\\d+(?=\\)?$)"),  # Zahl am Ende/vor Klammer-Ende
         short_name = str_remove(party_orig_value,
                                 "\\s*\\d+\\)?$"), # - Leerzeichen/Zahl/Klammer
         long_name = NA_character_, 
         prefix = NA_character_,
         level = NA_character_,
         source = "Gemeinde_Exekutiven") %>% 
  # Großschreibung der short_name-Spalte
  mutate(short_name = toupper(short_name))%>% 
  select(party_orig_value,
         short_name,
         long_name,
         prefix,
         add_info,
         level,
         source)
  



# Output prüfen (unique/distinct)
lookup_gemeinde_exekutiven_parties_label %>% distinct() %>% print(n = Inf)
# A tibble: 36 × 7
   party_orig_value short_name long_name prefix add_info level source           
   <chr>            <chr>      <chr>     <chr>  <chr>    <chr> <chr>            
 1 FDP 1            FDP        <NA>      <NA>   1        <NA>  Gemeinde_Exekuti…
 2 Die Mitte 2      DIE MITTE  <NA>      <NA>   2        <NA>  Gemeinde_Exekuti…
 3 CVP 2            CVP        <NA>      <NA>   2        <NA>  Gemeinde_Exekuti…
 4 SP               SP         <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
 5 SVP              SVP        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
 6 LP 1             LP         <NA>      <NA>   1        <NA>  Gemeinde_Exekuti…
 7 EVP              EVP        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
 8 CSP              CSP        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
 9 GLP              GLP        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
10 PdA              PDA        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
11 GRÜNE 3          GRÜNE      <NA>      <NA>   3        <NA>  Gemeinde_Exekuti…
12 EDU              EDU        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
13 Lega             LEGA       <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
14 Übrige           ÜBRIGE     <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
15 Total            TOTAL      <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
16 BDP 2            BDP        <NA>      <NA>   2        <NA>  Gemeinde_Exekuti…
17 FDP 1)           FDP        <NA>      <NA>   1        <NA>  Gemeinde_Exekuti…
18 CVP              CVP        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
19 LP 1)            LP         <NA>      <NA>   1        <NA>  Gemeinde_Exekuti…
20 BDP              BDP        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
21 GPS              GPS        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
22 Dem.             DEM.       <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
23 LdU              LDU        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
24 DSP              DSP        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
25 PSA              PSA        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
26 POCH             POCH       <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
27 GA               GA         <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
28 Sol.             SOL.       <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
29 SD               SD         <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
30 Rep.             REP.       <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
31 FPS              FPS        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
32 MCR              MCR        <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
33 LS               LS         <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
34 JB               JB         <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
35 Front            FRONT      <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…
36 Grüt             GRÜT       <NA>      <NA>   <NA>     <NA>  Gemeinde_Exekuti…

3.3 Parteilandschaft

Code anzeigen
#| label: load_parties_rating

parties_rating <- read_excel("~/CAS/Zertifikatsarbeit/data/parties_economic_socio-political_rating.xlsx",
                             sheet = "matrix_eco-socio_enhanced")

3.4 Lookup tables

3.4.1 Kantone

Code anzeigen
lookup_elec_nr_sr_combined_canton_label
# A tibble: 26 × 2
   CantonAbbreviation CantonName 
   <chr>              <chr>      
 1 VS                 Wallis     
 2 GE                 Genf       
 3 ZG                 Zug        
 4 ZH                 Zürich     
 5 FR                 Freiburg   
 6 BS                 Basel-Stadt
 7 BE                 Bern       
 8 AG                 Aargau     
 9 VD                 Waadt      
10 SZ                 Schwyz     
# ℹ 16 more rows

3.4.2 Parteinamen

Die verschiedenen Quellen verwenden teils unteschiedliche Parteibezeichnungen. Zur Vereinheitlichung wird deshalb initial eine Lookup Tabelle gebildet.

Code anzeigen
# National- und Ständerat
print(lookup_elec_nr_sr_combined_parties_label %>%
        distinct(),
      n = 3)
# A tibble: 20 × 7
  party_orig_value short_name long_name             prefix add_info level source
  <chr>            <chr>      <chr>                 <chr>  <lgl>    <chr> <chr> 
1 SVP              SVP        Schweizerische Volks… <NA>   NA       <NA>  Natio…
2 FDP-Liberale     FDP        FDP.Die Liberalen     <NA>   NA       <NA>  Natio…
3 SP               SP         Sozialdemokratische … <NA>   NA       <NA>  Natio…
# ℹ 17 more rows
Code anzeigen
# Kantonale Regierung
print(lookup_kantonale_regierung_parties_label %>%
        distinct(),
      n = 3)
# A tibble: 42 × 7
  party_orig_value short_name long_name prefix add_info level source            
  <chr>            <chr>      <chr>     <chr>  <chr>    <chr> <chr>             
1 FDP 2            FDP        <NA>      <NA>   2        <NA>  Kantonale_Regieru…
2 SP               SP         <NA>      <NA>   <NA>     <NA>  Kantonale_Regieru…
3 SVP              SVP        <NA>      <NA>   <NA>     <NA>  Kantonale_Regieru…
# ℹ 39 more rows
Code anzeigen
# Gemeinde Exekutiven
print(lookup_gemeinde_exekutiven_parties_label %>%
        distinct(),
      n = 3)
# A tibble: 36 × 7
  party_orig_value short_name long_name prefix add_info level source            
  <chr>            <chr>      <chr>     <chr>  <chr>    <chr> <chr>             
1 FDP 1            FDP        <NA>      <NA>   1        <NA>  Gemeinde_Exekutiv…
2 Die Mitte 2      DIE MITTE  <NA>      <NA>   2        <NA>  Gemeinde_Exekutiv…
3 CVP 2            CVP        <NA>      <NA>   2        <NA>  Gemeinde_Exekutiv…
# ℹ 33 more rows
Code anzeigen
# Abstimmungen
print(lookup_voting_parties_label %>%
        distinct(),
      n = 3)
# A tibble: 497 × 7
  party_orig_value short_name long_name prefix add_info level source      
  <chr>            <chr>      <chr>     <chr>  <chr>    <chr> <chr>       
1 p-fdp            FDP        <NA>      p      <NA>     state Abstimmungen
2 p-sps            SPS        <NA>      p      <NA>     state Abstimmungen
3 p-svp            SVP        <NA>      p      <NA>     state Abstimmungen
# ℹ 494 more rows
Code anzeigen
# Alle Dataframes zu einem zusammenführen und Duplikate entfernen
lookup_all_parties <- bind_rows(
  lookup_elec_nr_sr_combined_parties_label,
  lookup_kantonale_regierung_parties_label,
  lookup_gemeinde_exekutiven_parties_label,
  lookup_voting_parties_label)%>%
  distinct() %>% 
  left_join(lookups_codebook_parties %>%
              select(short_name,
                     Partei,
                     Parteiname),
            by = "short_name") %>%
  select(Partei,
         Parteiname,
         everything())

4 Transformation & visualization

4.1 Abstimmungen

4.2 Parteilandschaft

4.2.1 Multi-Dimensions-Model

4.2.1.1 Minimalwerte

Für jede Partei den Minimalwert des Abstimmungsverhaltens zu verwenden, basiert auf der Zielsetzung, die klarste politische Position einer Partei in einem bestimmten Themenbereich zu identifizieren. Diese Methodik stellt sicher, dass auch bei wenigen Abweichungen von der Mehrheitslinie die tatsächliche Haltung der Partei deutlich erkennbar bleibt.

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_min_x,
           y = socio_pct_min_y,
           label = Partei)) +
  geom_point(size = 3) +
  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(0, 100)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Grundlage: Minimalwerte des Abstimmungsverhaltens") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 105),
                  xlim = c(0, 105),
                  clip = "off")

4.2.1.2 Durchschnittswerte als Zentrum und Min/Max als Ellipse

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_avg_x,
           y = socio_pct_avg_y,
           label = Partei)) +
  geom_ellipse(
    aes(x0 = eco_pct_avg_x,
        y0 = socio_pct_avg_y,
        a = (eco_pct_max_x - eco_pct_min_x) / 2, # Halbachse x
        b = (socio_pct_max_y - socio_pct_min_y) / 2, # Halbachse y
        angle = 0),
    fill = "gray80", alpha = 0.4) +
  geom_point(size = 3) +
  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(-10, +110)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Grundlage: Durchschnitt des Abstimmungsverhaltens als Zentrum, Min-/Max als Ellipse") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 100),
                  xlim = c(0, 100),
                  clip = "off")

4.2.1.3 Hauptparteien: Durchschnittswerte als Zentrum und Min/Max als Ellipse / Klein- und Regionalparteien nur mit ihrem Durchschnittswert

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_avg_x,
           y = socio_pct_avg_y,
           label = Partei)) +
  geom_ellipse(aes(x0 = eco_pct_avg_x,
                   y0 = socio_pct_avg_y,
                   a = (eco_pct_max_x - eco_pct_min_x) / 2, # Halbachse x
                   b = (socio_pct_max_y - socio_pct_min_y) / 2, # Halbachse y
                   angle = 0),
               fill = "gray80", alpha = 0.4) +
  geom_point(size = 3) +
  
  # Kleinere Parteien
  geom_point(data = subset(parties_rating,
                           `Grosse Partei` == "n" &
                             Relevanz_nationale_Abstimmungen != "-"),
             aes(x = eco_x,
                 y = socio_y),
             shape = 21, fill = "grey", color = "black", size = 1, stroke = 1) +
  
  # Labels der kleineren Partein
  geom_text(data = subset(parties_rating,
                          `Grosse Partei` == "n" &
                            Relevanz_nationale_Abstimmungen != "-"),
            aes(x = eco_x,
                y = socio_y,
                label = Partei),
            vjust = -1, fontface = "plain", color = "darkgrey") +
  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(-10, +110)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Durchschnitt des Abstimmungsverhaltens als Zentrum, Min-/Max als Ellipse") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 100),
                  xlim = c(0, 100),
                  clip = "off")

4.2.1.4 Hauptparteien: Durchschnittswerte als Zentrum und Min/Max als Ellipse / Verbände sowie Klein- und Regionalparteien nur mit ihrem Durchschnittswert

Code anzeigen
ggplot(parties_rating,
       aes(x = eco_pct_avg_x,
           y = socio_pct_avg_y,
           label = Partei)) +
  geom_ellipse(
    aes(x0 = eco_pct_avg_x,
        y0 = socio_pct_avg_y,
        a = (eco_pct_max_x - eco_pct_min_x) / 2, # Halbachse x
        b = (socio_pct_max_y - socio_pct_min_y) / 2, # Halbachse y
        angle = 0),
    fill = "gray80", alpha = 0.4) +
  geom_point(size = 3) +
  
  # Kleinere Parteien
  geom_point(data = subset(parties_rating,
                           `Grosse Partei` == "n" #&
                           #Relevanz_nationale_Abstimmungen != "-"
                           )
                  ,
    aes(x = eco_x, y = socio_y),
    shape = 21, fill = "grey", color = "black", size = 1, stroke = 1
  ) +
  # Labels der kleineren Partein
  geom_text(data = subset(parties_rating,
                          `Grosse Partei` == "n" #&
                          #Relevanz_nationale_Abstimmungen != "-"
                  )
                  ,
    aes(x = eco_x, y = socio_y, label = Partei),
    vjust = -1, fontface = "plain", color = "darkgrey"
  ) +

  geom_text(vjust = -0.8) +
  geom_segment(aes(x = 50, xend = 50,
                   y = 0, yend = 100), linetype = "dashed", color = "blue") +
  geom_segment(aes(x = 0, xend = 100,
                   y = 50, yend = 50), linetype = "dashed", color = "blue") +
  annotate("text",
           x = 0,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Links/staatsgläubig", size = 4, hjust = 0.5) +
  annotate("text",
           x = 100,
           y = min(parties_rating$socio_pct_min_y) + 55,
           label = "Rechts/marktwirtschaftlich", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) - 3,
           label = "Autoritär/Konservativ", size = 4, hjust = 0.5) +
  annotate("text",
           x = 50,
           y = min(parties_rating$socio_pct_min_y) + 103,
           label = "Libertär/Progressiv", size = 4, hjust = 0.5) +
  scale_x_continuous(limits = c(-10, +110)) +
  labs(x = "Wirtschaftspolitisch",
       y = "Gesellschaftspolitisch",
       title = "Politische Positionierung im Zwei-Achsen-Modell",
       subtitle = "Grundlage: Durchschnitt des Abstimmungsverhaltens als Zentrum, Min-/Max als Ellipse") +
  theme_minimal()+
  theme(plot.title = element_text(size = 18,
                                  margin = margin(b = 30)),
        axis.title.x = element_text(size = 14,
                                    margin = margin(t = 5)),
        axis.title.y = element_text(size = 14,
                                    margin = margin(r = 5))) +
  coord_cartesian(ylim = c(0, 100),
                  xlim = c(0, 100),
                  clip = "off")

4.2.2 3D-Koordinatensystem (Testweise)

Code anzeigen
#install.packages("plotly")

library(plotly)

# Datenframe erstellen
parties <- data.frame(
  Partei = c("SP", "SVP", "FDP", "GPS", "GLP", "Mitte", "EVP", "EDU"),
  Wirtschaft = c(20, 85, 80, 30, 60, 60, 40, 90),      # Links = niedrig, Rechts = hoch
  Gesellschaft = c(90, 20, 55, 90, 75, 45, 50, 20),    # Libertär = hoch, Autoritär = niedrig
  Kosmopolitismus = c(90, 15, 60, 95, 85, 50, 55, 20)  # Kosmopolitisch = hoch, Nationalistisch = niedrig
)

# Interaktives 3D-Scatterplot mit Plotly
fig <- plot_ly(
  data = parties,
  x = ~Wirtschaft,
  y = ~Gesellschaft,
  z = ~Kosmopolitismus,
  text = ~Partei,
  type = 'scatter3d',
  mode = 'markers+text',
  marker = list(size = 5),
  textposition = 'top center'
)

# Achsen benennen und Layout anpassen
fig <- fig %>% layout(
  scene = list(
    xaxis = list(title = "Wirtschaftspolitik (links – rechts)"),
    yaxis = list(title = "Gesellschaftspolitik (autoritär – libertär)"),
    zaxis = list(title = "Kosmopolitismus – Nationalismus")
  ),
  title = "Schweizer Parteien im 3D-Koordinatensystem nach Kitschelt"
)

# Plot anzeigen
fig